import * as THREE from "three";
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import Stats from "three/addons/libs/stats.module.js";
import { GUI } from "three/addons/libs/lil-gui.module.min.js"
import TWEEN, { Tween } from "three/addons/libs/tween.module.js";
// 引入 Three 插件时，如果编辑器引入的是 three/examples/jsm/ 开头，把它替换为 three/addons
import { EXRLoader } from "three/addons/Addons.js";
import Logger from "./Logger";
import Coli from "./Coli";
import { BraceleData, BeadData } from "./BraceleData";

/**
 * 手串3D类
 * 可用接口:
 *  `constructor(canvas: HTMLCanvasElement, data: BraceleData, sceneMaps: {[key: 'dark'|'light']: string}): Bracele3D` - 创建并初始化场景，同时初始化线圈(coli)，默认场景为白天。
 *  `addBead(beadData: BeadData): number` - 添加一个珠子，返回所有珠子已经使用的长度。
 *  `canBackout(): boolean` - 判断是否可以撤销一个珠子。
 *  `backout(): number` - 撤销一个珠子，返回所有珠子已经使用的长度。
 *  `setColiColor(color: number): void` - 设置线圈颜色，颜色参数为十六进制数（例如：0xffffff）。
 *  `isNight: boolean` - 表示当前是否为夜晚模式。
 *  `changeNight(): void` - 切换白天/夜晚模式。
 *  `setNight(isNight: boolean = true): void` - 设置白天/夜晚模式。
 *  `reset(): number` - 重置所有操作，返回所有珠子已经使用的长度。
 *  `canCut(): boolean` - 判断是否可以裁切线圈。
 *  `cut(): number` - 裁切线圈，将多余的部分剔除，将当前珠子所用尺寸设置为周长，返回所有珠子已经使用的长度（当前线圈周长）。
 *  `setDebug(isDebug: boolean = true): void` - 开启或关闭调试模式。
 *  `setGUI(isGUIOpen: boolean = true): void` - 开启或关闭图形用户界面。
 *  `getData(): BraceleData` - 获取当前的场景数据，可用于存储到数据库，下次直接传入到 `Bracele3D` 构造函数中即可复原场景。
 *  `move(direction: 'up' | 'down' | 'left' | 'right', distance: number = 1): void` - 控制相机上下左右移动。
 *  `zoomIn(factor: number = 1.1): void` - 放大相机视角。
 *  `zoomOut(factor: number = 1.1): void` - 缩小相机视角。
 *  `resetView(): void` - 复原相机视角到初始位置。
 *  `rotateForward(angle: number = Math.PI / 6): void` - 使线圈正向旋转指定角度。
 *  `rotateBackward(angle: number = Math.PI / 6): void` - 使线圈反向旋转指定角度。
 */
export default class Bracele3D {
    #cameraFov = 75;
    #cameraNear = 0.1;
    #cameraFar = 1000;
    #cameraPosZ = 5;
    #camera = null;
    /** @type {Coli|null} */
    coli = null; // 线圈对象
    #loadedEnvMap = {};
    #initedData = null;
    isNight = false;
    #tweens = {};

    /**
     * @param {HTMLCanvasElement} canvas 3D效果显示的目标画布
     * @param {BraceleData} data 所有场景数据
     * @param {{[key: 'dark'|'light']: string}} sceneMaps 场景资源路径{dark: 暗色模式资源路径, light: 亮色模式资源路径}
     */
    constructor(canvas, data, sceneMaps) {
        this.canvas = canvas;
        this.sceneMaps = sceneMaps;
        // 深度克隆数据
        this.#initedData = JSON.parse(JSON.stringify(data));
        this.scene = new THREE.Scene();

        this._initCamera();
        this._loadEnv();
        this._initRenderer();
        this._initControls(data);
        this._initColi(data);

        this.render();
    }

    _initCamera() {
        // camera
        this.#camera = new THREE.PerspectiveCamera(this.#cameraFov,
            this.canvas.clientWidth / this.canvas.clientHeight,
            this.#cameraNear,
            this.#cameraFar
        );

        this.#camera.position.z = this.#cameraPosZ;
    }

    _loadEnv() {
        // 主环境光 - 模拟自然环境光，稍微提高强度补偿移除的辅助光
        this.ambientLight = new THREE.AmbientLight(0xffffff, 0.6);

        // 主平行光 - 模拟太阳/月亮光源，略微增强以补偿
        this.directionalLight = new THREE.DirectionalLight();
        this.directionalLight.position.set(50, 50, 0);
        this.directionalLight.lookAt(new THREE.Vector3(0, 0, 0));

        this.scene.add(this.ambientLight);
        this.scene.add(this.directionalLight);

        this.setNight(false);
    }

    _initRenderer() {
        this.renderer = new THREE.WebGLRenderer({
            canvas: this.canvas,
            antialias: true,    // 抗锯齿
            logarithmicDepthBuffer: true,   // 启用对数深度缓冲区
        });

        // 添加：设置渲染器尺寸
        this.renderer.setSize(this.canvas.clientWidth, this.canvas.clientHeight);
        // 设置设备像素比
        this.renderer.setPixelRatio(window.devicePixelRatio);
    }

    _initControls(data) {
        // controls
        this.controls = new OrbitControls(this.#camera, this.renderer.domElement);
        this.controls.target.set(0, 0, 0);
        this.controls.enablePan = false;    // 禁止平移

        const circumference = data.coli.circumference;
        const radius = circumference / 2 / Math.PI;
        const halfFovRad = (this.#cameraFov * Math.PI / 180) / 2;
        const requiredZ = radius * 4 / Math.tan(halfFovRad);

        // 设置相机的 Z 轴位置
        this.#camera.position.z = requiredZ;
        this.#cameraPosZ = requiredZ;
        console.log(this.#camera.position.z);

        this.controls.minDistance = this.#cameraPosZ * 0.5;  // 可以放大到原始大小的 2 倍
        this.controls.maxDistance = this.#cameraPosZ * 2;     // 可以缩小到原始大小的 0.5 倍
        this.controls.enableDamping = true;
        this.controls.dampingFactor = 0.25;
    }

    render() {
        if (this.isDebug) {
            this.stats.update();
        }

        requestAnimationFrame(this.render.bind(this));
        for (let key in this.#tweens) {
            const tween = this.#tweens[key];
            tween.update();
        }

        this.controls.update();
        this.renderer.render(this.scene, this.#camera);
    }

    _initColi(data) {
        this.coli = new Coli(data.coli);

        const beads = data.beads;
        for (let i = 0; i < beads.length; i++) {
            this.addBead(beads[i]);
        }

        this.scene.add(this.coli.mesh);
    }

    /**
     * 添加一个珠子
     * @param {BeadData} beadData 珠子参数
     * @returns {number} 所有珠子已经使用的长度
     */
    addBead(beadData) {
        return this.coli.addBead(beadData);
    }

    /**
     * @returns {boolean} 是否可以撤销一个珠子
     */
    canBackout() {
        return this.coli.canBackout();
    }

    /**
     * 撤销一个珠子
     * @returns {number} 所有珠子已经使用的长度
     */
    backout() {
        return this.coli.backout();
    }

    /**
     * 切换黑暗模式
     */
    changeNight() {
        this.setNight(!this.isNight);
    }

    /**
     * 黑暗模式开关
     * @param boolean isNight 是否是黑暗模式
     */
    setNight(isNight = true) {
        this.isNight = isNight;
        this._setEnvMap(isNight);
        if (this.isNight) {
            // 夜晚模式
            // 调整主环境光，使用偏蓝的冷色调
            this.ambientLight.intensity = 0.3;
            this.ambientLight.color.set(0x4B6584);  // 深蓝灰色

            // 调整平行光模拟月光，稍微增强以弥补移除的辅助光
            this.directionalLight.intensity = 0.4;
            this.directionalLight.color.set(0xCFD9DF);  // 偏蓝的月光色
            this.directionalLight.castShadow = true;

            // 深色背景
            this.scene.background = new THREE.Color(0x1B1B1B);
        } else {
            // 白天模式
            // 调整主环境光
            this.ambientLight.intensity = 0.6;
            this.ambientLight.color.set(0xffffff);  // 改为灰色

            // 调整平行光模拟阳光
            this.directionalLight.intensity = 1.2;
            this.directionalLight.color.set(0xffffff);  // 改为灰色

            // 明亮背景
            this.scene.background = new THREE.Color(0xCCCCCC);  // 背景也改为灰色
        }
    }

    _setEnvMap(isNight) {
        if (this.#loadedEnvMap[isNight]) {
            this.scene.background = this.#loadedEnvMap[isNight];
            return;
        }

        const exrLoader = new EXRLoader();
        exrLoader.load(this.sceneMaps[isNight ? 'dark' : 'light'], (environmentMap) => {
            environmentMap.mapping = THREE.EquirectangularRefractionMapping;
            this.#loadedEnvMap[isNight] = environmentMap;
            this.scene.backgroundBlurriness = 0;
            this.scene.environment = environmentMap;
        });
    }

    /**
     * 设置线圈颜色
     * @param {number} color 线圈颜色,十六进制(example: 0xffffff)
     */
    setColiColor(color) {
        this.coli.setColor(color);
    }

    /**
     * 是否可以裁切线圈
     * @returns {boolean} 是否可以裁切
     */
    canCut() {
        return this.coli.canCut();
    }

    /**
     * 裁切线圈，将多余的部分剔除，将当前珠子所用尺寸设置为周长
     * @returns {number} 所有珠子已经使用的长度(当前线圈周长)
     */
    cut() {
        return this.coli.cut();
    }

    setDebug(isDebug = true) {
        if (isDebug === this.isDebug) {
            return;
        }

        this.isDebug = isDebug;  // 直接设置为传入的值
        if (this.isDebug) {
            this.stats = new Stats();
            document.body.appendChild(this.stats.domElement);
            this.directionalLightHelper = new THREE.DirectionalLightHelper(this.directionalLight, 5);
            this.scene.add(this.directionalLightHelper);
            this.setGUI(true);
        } else if (this.stats) {  // 确保 stats 存在才移除
            this.directionalLightHelper.removeFromParent();
            this.directionalLightHelper = null;
            document.body.removeChild(this.stats.domElement);
            this.stats = null;  // 清除引用
        }
    }

    setGUI(isGUIOpen = true) {
        if (isGUIOpen == this.isGUIOpen) {
            Logger.log("gui status not changed");
            return;
        }

        this.isGUIOpen = isGUIOpen;
        if (this.isGUIOpen) {
            this.gui = new GUI();
            let cameraGUI = this.gui.addFolder("相机");
            cameraGUI.add(this.#camera.position, "z", 1, 100).step(1);
            cameraGUI.close();

            const renderGUI = this.gui.addFolder("渲染");
            renderGUI.add(this.renderer, "toneMappingExposure", -100, 100);
            renderGUI.close();

            let sceneGUI = this.gui.addFolder("场景");
            sceneGUI.addColor(this.scene, "background").name("背景颜色");
            sceneGUI.close();

            let ambientLightGUI = this.gui.addFolder("主环境灯");
            ambientLightGUI.add(this.ambientLight, "visible");
            ambientLightGUI.addColor(this.ambientLight, "color").name("主环境光颜色");
            ambientLightGUI.add(this.ambientLight, "intensity", 0, 20).name("主环境光强度");
            ambientLightGUI.close();

            let directionalLightGUI = this.gui.addFolder("平行光");
            directionalLightGUI.add(this.directionalLight, "visible");
            directionalLightGUI.addColor(this.directionalLight, "color").name("平行光颜色");
            directionalLightGUI.add(this.directionalLight, "intensity", 0, 20).name("平行光强度");
            directionalLightGUI.close();
        } else if (this.gui) {
            this.gui.destroy();
        }
    }

    /**
     * 重置所有操作
     * @returns {number} 所有珠子已经使用的长度
     */
    reset() {
        return this.coli.setData(this.#initedData);
    }

    /**
     * 获取当前的场景数据，可用于存储到数据库,下次直接传入到Bracele3D构造函数中，即可复原场景
     * @returns {BraceleData} 场景数据
     */
    getData() {
        return this.coli.getData();
    }

    /**
     * 控制相机上下左右移动
     * @param {'up' | 'down' | 'left' | 'right'} direction - 移动方向
     * @param {number} distance - 移动距离
     */
    move(direction, distance = 1) {
        const camera = this.#camera;
        const delta = new THREE.Vector3();
        const controlsTarget = this.controls.target;

        switch (direction) {
            case 'up':
                delta.set(0, distance, 0);
                break;
            case 'down':
                delta.set(0, -distance, 0);
                break;
            case 'left':
                delta.subVectors(camera.position, controlsTarget).normalize();
                delta.cross(camera.up).multiplyScalar(-distance);
                break;
            case 'right':
                delta.subVectors(camera.position, controlsTarget).normalize();
                delta.cross(camera.up).multiplyScalar(distance);
                break;
            default:
                console.warn('无效的移动方向');
                return;
        }

        this.#tweens['move'] = new Tween({
            camera: camera.position,
            controls: this.controls.target
        });
        this.#tweens['move'].to({
            camera: new THREE.Vector3(camera.position.x + delta.x, camera.position.y + delta.y, camera.position.z + delta.z),
            controls: new THREE.Vector3(controlsTarget.x + delta.x, controlsTarget.y + delta.y, controlsTarget.z + delta.z)
        }, 300).easing(TWEEN.Easing.Quadratic.InOut).start();
        this.controls.update();
    }

    /**
     * 放大相机视角
     * @param {number} factor - 放大倍数，默认为 1.1
     */
    zoomIn(factor = 1.1) {
        const newDistance = this.controls.getDistance() / factor;
        if (newDistance >= this.controls.minDistance) {
            this.#tweens['zoom'] = new Tween(this.#camera.position)
                .to({
                    x: this.#camera.position.x / factor,
                    y: this.#camera.position.y / factor,
                    z: this.#camera.position.z / factor
                }, 300)
                .easing(TWEEN.Easing.Quadratic.InOut)
                .start();
            this.controls.update();
        }
    }

    /**
     * 缩小相机视角
     * @param {number} factor - 缩小倍数，默认为 1.1
     */
    zoomOut(factor = 1.1) {
        const newDistance = this.controls.getDistance() * factor;
        if (newDistance <= this.controls.maxDistance) {
            // this.camera.position.multiplyScalar(factor);
            // 使用 Tween 库实现相机缩小视角的动画
            this.#tweens['zoom'] = new Tween(this.#camera.position)
                .to({
                    x: this.#camera.position.x * factor,
                    y: this.#camera.position.y * factor,
                    z: this.#camera.position.z * factor
                }, 300)
                .easing(TWEEN.Easing.Quadratic.InOut)
                .start();
            this.controls.update();
        }
    }

    /**
     * 复原相机视角到初始位置
     */
    resetView() {
        // 使用 Tween 库实现相机和线圈的动画复原
        this.#tweens['resetView'] = new TWEEN.Tween({
            cameraX: this.#camera.position.x,
            cameraY: this.#camera.position.y,
            cameraZ: this.#camera.position.z,
            controlsX: this.controls.target.x,
            controlsY: this.controls.target.y,
            controlsZ: this.controls.target.z,
            coliRotation: this.coli && this.coli.mesh ? this.coli.mesh.rotation.y : 0
        }).to({
            cameraX: 0,
            cameraY: 0,
            cameraZ: this.#cameraPosZ,
            controlsX: 0,
            controlsY: 0,
            controlsZ: 0,
            coliRotation: 0
        }, 300).easing(TWEEN.Easing.Quadratic.InOut)
            .onUpdate(({ cameraX, cameraY, cameraZ, controlsX, controlsY, controlsZ, coliRotation }) => {
                this.#camera.position.set(cameraX, cameraY, cameraZ);
                this.controls.target.set(controlsX, controlsY, controlsZ);
                if (this.coli && this.coli.mesh) {
                    this.coli.mesh.rotation.y = coliRotation;
                }
            })
            .onComplete(() => {
                this.controls.update();
            })
            .start();
    }

    rotate(angle) {
        if (this.coli && this.coli.mesh) {
            // 使用 Tween 库实现线圈旋转动画
            this.#tweens['rotate'] = new TWEEN.Tween({ rotation: this.coli.mesh.rotation.y })
                .to({ rotation: this.coli.mesh.rotation.y + angle }, 300)
                .easing(TWEEN.Easing.Quadratic.InOut)
                .onUpdate(({ rotation }) => {
                    this.coli.mesh.rotation.y = rotation;
                })
                .start();
            // this.coli.mesh.rotation.y += angle;
        }
    }

    /**
     * 使线圈正向旋转指定角度
     * @param {number} angle - 旋转角度，单位为弧度
     */
    rotateForward(angle = Math.PI / 6) {
        this.rotate(angle);
    }

    /**
     * 使线圈反向旋转指定角度
     * @param {number} angle - 旋转角度，单位为弧度
     */
    rotateBackward(angle = Math.PI / 6) {
        this.rotate(-angle);
    }
}
